package com.ryx.service.Impl;

import com.github.pagehelper.PageHelper;
import com.ryx.constants.Constant;
import com.ryx.entity.SysRole;
import com.ryx.exception.GlobalException;
import com.ryx.exception.code.BaseResponseCode;
import com.ryx.mapper.SysRoleMapper;
import com.ryx.mapper.SysRolePermissionMapper;
import com.ryx.service.PermissionService;
import com.ryx.service.RolePermissionService;
import com.ryx.service.RoleService;
import com.ryx.service.UserRoleService;
import com.ryx.utils.PageUtil;
import com.ryx.utils.RedisUtil;
import com.ryx.utils.TokenSetting;
import com.ryx.vo.req.AddRoleReqVo;
import com.ryx.vo.req.RolePageReqVo;
import com.ryx.vo.req.RolePermissionOperationReqVo;
import com.ryx.vo.req.RoleUpdateReqVo;
import com.ryx.vo.resp.PageVo;
import com.ryx.vo.resp.PermissionRespNodeVo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.xml.crypto.Data;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @BelongsPackage: com.ryx.service.Impl
 * @Author: 容永轩
 * @CreateTime: 2020-12-27
 * @Description:
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class RoleServiceImpl implements RoleService {

    @Autowired
    private SysRoleMapper sysRoleMapper;

    @Autowired
    private RolePermissionService rolePermissionService;

    @Autowired
    private PermissionService permissionService;

    @Autowired
    private UserRoleService userRoleService;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private TokenSetting tokenSetting;

    @Override
    public PageVo<SysRole> pageInfo(RolePageReqVo vo) {
        //这里用startPage 而不是offsetPage 他们两者下标对应的是不一样的 一个是0 一个是1
        PageHelper.startPage(vo.getPageNum(),vo.getPageSize());

        List<SysRole> sysRoles = sysRoleMapper.selectAll(vo);


        return PageUtil.getPageVo(sysRoles);
    }

    @Override
    public SysRole addRole(AddRoleReqVo vo) {

        SysRole sysRole = new SysRole();
        BeanUtils.copyProperties(vo,sysRole);

        sysRole.setId(UUID.randomUUID().toString());
        sysRole.setCreateTime(new Date());

        int i = sysRoleMapper.insertSelective(sysRole);
        if (i !=1){
            throw new GlobalException(BaseResponseCode.DATA_ERROR);
        }

        //中间表关联关系处理
        //新增
        if (vo.getPermissions() != null && !vo.getPermissions().isEmpty()){
            RolePermissionOperationReqVo operationReqVo = new RolePermissionOperationReqVo();

            operationReqVo.setRoleId(sysRole.getId());
            operationReqVo.setPermissionIds(vo.getPermissions());
            //去创建service层关联表接口 RolePermissionService 数据加入到中间表sys_role_permission
            rolePermissionService.addRolePermission(operationReqVo);
        }



        return sysRole;
    }

    @Override
    public List<SysRole> selectAll() {
        //传一个空的对象进去即可，查询所有的数据就可以直接封装在赋予角色框中
        return sysRoleMapper.selectAll(new RolePageReqVo());
    }

    @Override
    public SysRole detailInfo(String id) {
        //通过id获取角色信息
        SysRole sysRole = sysRoleMapper.selectByPrimaryKey(id);
        if (sysRole == null){
            throw new GlobalException(BaseResponseCode.DATA_ERROR);
        }

        //获取所有菜单权限树
        List<PermissionRespNodeVo> permissionRespNodeVos = permissionService.selectAllTree();
        //获取该角色拥有的菜单权限  根据角色id获取该角色关联的菜单权限id集合
        List<String> permissionIdsByRoleId = rolePermissionService.getPermissionIdsByRoleId(id);

        Set<String> checkList = new HashSet<>(permissionIdsByRoleId);

        //遍历菜单权限树的数据
        setChecked(permissionRespNodeVos,checkList);
        sysRole.setPermissionRespNode(permissionRespNodeVos);
        return sysRole;

    }

    private void setChecked(List<PermissionRespNodeVo> permissionRespNodeVos,Set<String> checkList){
        for(PermissionRespNodeVo permissionRespNodeVo:permissionRespNodeVos){
            /**
             * 子集选中从它往上到跟目录都被选中，父级选中从它到它所有的叶子节点都会被选中
             * 这样我们就直接遍历最底层及就可以了
             * 我的理解是 用根据角色id获取该角色拥有的菜单权限id集合 与全部的菜单权限作比较 拥有的就点上,而不是点击父级下面全部都点上
             */
            if(checkList.contains(permissionRespNodeVo.getId())&&(permissionRespNodeVo.getChildren()==null||permissionRespNodeVo.getChildren().isEmpty())){
                permissionRespNodeVo.setChecked(true);
            }
            //递归
            setChecked((List<PermissionRespNodeVo>) permissionRespNodeVo.getChildren(),checkList);

        }
    }


    @Override
    public void updateRole(RoleUpdateReqVo vo) {
        //通过vo.id查询到当前要修改角色信息的用户data
        SysRole sysRole = sysRoleMapper.selectByPrimaryKey(vo.getId());

        if (sysRole == null){
            throw new GlobalException(BaseResponseCode.DATA_ERROR);
        }
        //把表单数据保存到reqVo
        BeanUtils.copyProperties(vo,sysRole);
        sysRole.setUpdateTime(new Date());
        int i = sysRoleMapper.updateByPrimaryKeySelective(sysRole);
        if (i !=1){
            throw new GlobalException(BaseResponseCode.OPERATION_ERROR);
        }

        //修改该用户角色和菜单权限的关联数据
        RolePermissionOperationReqVo reqVo = new RolePermissionOperationReqVo();
        reqVo.setRoleId(vo.getId());
        reqVo.setPermissionIds(vo.getPermissions());
        rolePermissionService.addRolePermission(reqVo);

        //标记关联用户
        List<String> userIdsBtRoleId = userRoleService.getUserIdsByRoleId(vo.getId());
        if(!userIdsBtRoleId.isEmpty()){
            for (String userId: userIdsBtRoleId) {
                /**
                 * 标记用户 在用户认证的时候判断这个是否主动刷过
                 */
                redisUtil.set(Constant.JWT_REFRESH_KEY+userId,userId,tokenSetting.getAccessTokenExpireTime().toMillis(), TimeUnit.MILLISECONDS);

                /**
                 * 清楚用户授权数据缓存
                 */
                redisUtil.delete(Constant.IDENTIFY_CACHE_KEY+userId);
            }
        }


    }

    @Override
    public void deletedRole(String roleId) {

        SysRole sysRole = new SysRole();
        sysRole.setId(roleId);
        sysRole.setDeleted(0);
        sysRole.setUpdateTime(new Date());

        int i = sysRoleMapper.updateByPrimaryKeySelective(sysRole);
        if(i !=1){
            throw new GlobalException(BaseResponseCode.OPERATION_ERROR);
        }

        //根据roleId删除角色菜单关联数据
        rolePermissionService.removeByRoleId(roleId);
        //根据roleId删除角色用户关联数据
        userRoleService.removeUserRoleId(roleId);

        //把跟该角色关联的用户标记起来 ，刷新token
        List<String> userIdsByRoleId = userRoleService.getUserIdsByRoleId(roleId);
        if(!userIdsByRoleId.isEmpty()){
            for (String userId: userIdsByRoleId) {
                /**
                 * 标记用户 在用户认证的时候判断这个是否主动刷过
                 */
                redisUtil.set(Constant.JWT_REFRESH_KEY+userId,userId,tokenSetting.getAccessTokenExpireTime().toMillis(), TimeUnit.MILLISECONDS);
                /**
                 * 清楚用户授权数据缓存
                 */
                redisUtil.delete(Constant.IDENTIFY_CACHE_KEY+userId);
            }
        }

    }

    //拿到用户角色信息
    @Override
    public List<String> getNamesByUserId(String userId) {
        //通过用户id 拿到角色id集合  在中间表中  通过userId把角色id集合查询出来
        List<String> roleIdsByUserId = userRoleService.getRoleIdsByUserId(userId);
        if(roleIdsByUserId.isEmpty()){
            return null;
        }

        //通过角色id集合 查到每个角色的Name
        return sysRoleMapper.selectNamesByIds(roleIdsByUserId);
    }


}
